/*
 * Copyright (C) 2008 Peter Kling
 *
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 *
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * 'A Kind of Billiard'. If not, see <http://www.gnu.org/licenses/>.
 */

package cg2.billiard;

import javax.media.opengl.GL;

import cg2.mathematics.MathVector;
import cg2.models.WavefrontObjLoader;
import cg2.physics.SimpleObstacle;

/**
 * @brief DBU normed 8-feet pool billiard table.
 * @author peter
 *
 */
public class DBUPoolTable8Feet extends BilliardTable {
	/**
	 * @brief Constructor for 8-feet DBU pool tables.
	 */
	public DBUPoolTable8Feet() {
		super();
		length = Billiard.DBU_POOLTABLE_8FEET_LENGTH;
		width  = Billiard.DBU_POOLTABLE_8FEET_WIDTH;
		height = Billiard.DBU_POOLTABLE_8FEET_HEIGHT;
	}
	
	/**
	 * @brief Loads the ball set for this billiard table.
	 */
	protected void initBalls() {
		cueBall = Billiard.getDBUPoolBalls_cueBall_8feet();
		balls   = Billiard.getDBUPoolBalls_triangle_8feet();
	}
	
	/**
	 * @brief Loads the standard pool ball models.
	 */
	protected void initBalls_models(GL gl) {
		cueBall.setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLCUEBALL));
		balls.get(0).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL01));
		balls.get(1).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL02));
		balls.get(2).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL03));
		balls.get(3).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL04));
		balls.get(4).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL05));
		balls.get(5).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL06));
		balls.get(6).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL07));
		balls.get(7).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL08));
		balls.get(8).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL09));
		balls.get(9).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL10));
		balls.get(10).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL11));
		balls.get(11).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL12));
		balls.get(12).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL13));
		balls.get(13).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL14));
		balls.get(14).setModel(WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_POOLBALL15));
	}
	
	/**
	 * @brief In addition to the standard borders, this method adds 'holes' for the pockets and an additional lower
	 *        plane that will catch any fallen ball.
	 */
	@Override
	protected void initBorderPlanes() {
		super.initBorderPlanes();
		
		double x_min, x_max, z_min, z_max;
		double y_min = Double.NEGATIVE_INFINITY;
		double y_max = Double.POSITIVE_INFINITY;
		double x_offset = 0.06, z_offset = 0.06;
		
		// add rifts to bottom plane
		x_min = -Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.-x_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max = -Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.-x_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min = Double.NEGATIVE_INFINITY;
		z_max = Double.POSITIVE_INFINITY;
		planes.get(4).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		x_min =  Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.+x_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max =  Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.+x_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		planes.get(4).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		x_min = Double.NEGATIVE_INFINITY;
		x_max = Double.POSITIVE_INFINITY;
		z_min = Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max = Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		planes.get(4).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		z_min = -Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max = -Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		planes.get(4).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// pocket 1 (upper-left)
		x_min = -Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.-x_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max = -Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.-x_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min = - Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max = - Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max += Billiard.DBU_POOLBALL_RADIUS;
		z_max += Billiard.DBU_POOLBALL_RADIUS;
		planes.get(0).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		planes.get(3).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// pocket 2 (upper-mid)
		x_min =  0. - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max =  0. + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min = - Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max = - Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max += Billiard.DBU_POOLBALL_RADIUS;
		planes.get(3).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// pocket 3 (upper-right)
		x_min =  Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.+x_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max =  Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.+x_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min = - Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max = - Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.-z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_min -= Billiard.DBU_POOLBALL_RADIUS;
		z_max += Billiard.DBU_POOLBALL_RADIUS;
		planes.get(2).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		planes.get(3).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// pocket 4 (lower-left)
		x_min = -Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.-x_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max = -Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.-x_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min =   Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max =   Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max += Billiard.DBU_POOLBALL_RADIUS;
		z_min -= Billiard.DBU_POOLBALL_RADIUS;
		planes.get(0).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		planes.get(1).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// pocket 5 (lower-mid)
		x_min =  0. - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max =  0. + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min =   Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max =   Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min -= Billiard.DBU_POOLBALL_RADIUS;
		planes.get(1).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// pocket 6 (lower-right)
		x_min =  Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.+x_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_max =  Billiard.DBU_POOLTABLE_8FEET_LENGTH/2.+x_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_min =   Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset - Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		z_max =   Billiard.DBU_POOLTABLE_8FEET_WIDTH/2.+z_offset + Billiard.DBU_POOLTABLE_8FEET_POCKETRADIUS;
		x_min -= Billiard.DBU_POOLBALL_RADIUS;
		z_min -= Billiard.DBU_POOLBALL_RADIUS;
		planes.get(1).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		planes.get(2).addHole(x_min, x_max, y_min, y_max, z_min, z_max);
		
		// add planes that boxes the table
		MathVector normal = new MathVector(3);
		double buffer = 4*Billiard.DBU_POOLBALL_RADIUS;
		normal.set(0, -1.); normal.set(1, 0.); normal.set(2, 0.);
		planes.add(new SimpleObstacle(normal, Billiard.DBU_POOLTABLE_8FEET_LENGTH/2. + buffer));
		normal.set(0, 0.); normal.set(1, 0.); normal.set(2, 1.);
		planes.add(new SimpleObstacle(normal, Billiard.DBU_POOLTABLE_8FEET_WIDTH/2. + buffer));
		normal.set(0, 1.); normal.set(1, 0.); normal.set(2, 0.);
		planes.add(new SimpleObstacle(normal, Billiard.DBU_POOLTABLE_8FEET_LENGTH/2. + buffer));
		normal.set(0, 0.); normal.set(1, 0.); normal.set(2, -1.);
		planes.add(new SimpleObstacle(normal, Billiard.DBU_POOLTABLE_8FEET_WIDTH/2. + buffer));
		normal.set(0, 0.); normal.set(1, -1.); normal.set(2, 0.);
		planes.add(new SimpleObstacle(normal, buffer));
	}

	/**
	 * @brief Loads the table model.
	 */
	protected void initTable_model(GL gl) {
		model = WavefrontObjLoader.loadObject(gl, Billiard.OBJECT_DBU_POOLTABLE_8FEET);
	}

	/**
	 * @brief Resets the billiard table by moving all balls back to the start position.
	 * 
	 * Furthermore, this method will reset standard parameters like the cue indicator.
	 */
	@Override
	public void reset() {
		super.reset();
		Billiard.setDBUPoolBalls_cueBall_8feet(cueBall);
		Billiard.setDBUPoolBalls_triangle_8feet(balls);
	}
	
	/**
	 * @brief Resets the cue ball of the billiard table
	 * 
	 * In addition to resetting the cue indicator, this method will also reset the position of the cue ball.
	 */
	@Override
	public void resetCueBall() {
		super.resetCueBall();
		Billiard.setDBUPoolBalls_cueBall_8feet(cueBall);
	}
}
